#include "gui.h"

static void
wait_end(GtkWidget *widget,
         gpointer data) {
    struct game_info *game_info = (struct game_info *)data;
    struct player *players = game_info->game->players;
    struct board *board = game_info->game->board;
    draw(players, board, game_info);
}

static void
quit(GtkWidget *widget,
     gpointer data) {
    struct game_info *game_info = (struct game_info *)data;
    struct args args = game_info->game->args;
    fclose(args.log_filep);
    board_destroy(game_info->game->board);
    for (int i = 0; i < args.n; i++) player_destroy(game_info->game->players + i);
    exit(0);
}

static void
play_gui_demo(GtkWidget *widget,
              gpointer data) {
    struct game_info *game_info = (struct game_info *)data;
    struct player *players = game_info->game->players;
    struct board *board = game_info->game->board;
    struct args args = game_info->game->args;
    FILE *fp = args.log_filep;
    if (game_info->card_idx == INIT) {
        board_reshuffle(board);
        if (args.mode == MODE_DEMO) disp_shuffle(board);
        log_shuffle(fp, board);

        disp_game_header(), log_game_header(fp);
        // Deal cards
        for (int i = 0; i < args.n; i++) {
            for (int j = 0; j < args.c; j++) {
                deal(board, &players[i]);
            }
            if (args.mode == MODE_DEMO) disp_draw_cards(&players[i], args.c);
            log_draw_cards(fp, &players[i], args.c);
        }

        // Draw first player
        if (board->next_player == NULL) {
            int min = CARD_PER_DECK + 1;
            for (int i = 0; i < args.n; i++) {
                card c = board_pop_card(board);
                // Show card c to player i
                disp_show_card(i, c), log_show_card(args.log_filep, i, c);
                if (c.order < min) {
                    min = c.order;
                    board->next_player = &(players[i]);
                }
                board_push_card(board, c);
            }
        }

        // Random flip
        card c = board_pop_card(board);
        disp_init_result(c), log_init_result(fp, c);
        board_push_card(board, c);
        draw(players, board, game_info);
    } else {
        struct player *cur_player = board->next_player;

        disp_turn_header(cur_player, board), log_turn_header(fp, cur_player, board);

        if (!player_can_pop(cur_player, board->top_card, board->force_draw)) {
            // draw cards
            if (!board->force_draw) board->force_draw = 1;
            for (int i = 0; i < board->force_draw; i++) deal(board, cur_player);
            disp_draw_cards(cur_player, board->force_draw), log_draw_cards(fp, cur_player, board->force_draw);
            board->force_draw = 0;
        } else {
            card c = {game_info->card_idx};
            if (!board_can_play(board, c) || !player_choose_card(cur_player, c)) return;
            discard(cur_player, board);
            disp_player_played(board, 1, cur_player->id), log_player_played(fp, board, 1, cur_player->id);
            board->force_draw += card_need_draw(board->top_card);
            if (card_is_cancel(board->top_card)) board->force_draw = 0;
        }

        if (cur_player->card_count == 0) {
            disp_game_summary(players, board, args);
            log_game_summary(fp, players, board, args);

            // Reset players and board
            for (int i = 0; i < args.n; i++) {
                players[i].score -= players[i].card_count;
                while (players[i].card_count) {
                    discard(&players[i], board);
                }
            }
            board_reset(board);
            draw_welcome(game_info);
        } else {
            board_next_player(board);
            if (args.mode == MODE_NORMAL)
                draw_wait(game_info);
            else
                draw(players, board, game_info);
        }
    }
}

void draw_welcome(struct game_info *game_info) {
    struct player *players = game_info->game->players;
    struct args args = game_info->game->args;
    GtkWidget *button, *label;
    gtk_container_remove(GTK_CONTAINER(game_info->game->window), game_info->game->grid);
    game_info->game->grid = gtk_grid_new();
    gtk_container_add(GTK_CONTAINER(game_info->game->window), game_info->game->grid);
    /* Pack the container in the window */

    button = gtk_button_new_with_label("New round");
    g_signal_connect(button, "clicked", G_CALLBACK(play_gui_demo), &(game_info->game_info[INIT]));

    /* Place the first button in the grid cell (0, 0), and make it fill
   * just 1 cell horizontally and vertically (ie no spanning)
   */
    gtk_grid_attach(GTK_GRID(game_info->game->grid), button, 0, 0, 1, 1);

    button = gtk_button_new_with_label("Quit");
    g_signal_connect(button, "clicked", G_CALLBACK(quit), game_info);

    /* Place the Quit button in the grid cell (0, 1), and make it
   * span 2 columns.
   */
    gtk_grid_attach(GTK_GRID(game_info->game->grid), button, 0, 1, 1, 1);

    for (int i = 0; i < args.n; i++) {
        char tmp[128];
        sprintf(tmp, "Player %d: %d, total: %d\n", i, -players[i].card_count, players[i].score - players[i].card_count);
        label = gtk_label_new(tmp);
        gtk_grid_attach(GTK_GRID(game_info->game->grid), label, 0, i + 2, 1, 1);
    }

    /* Now that we are done packing our widgets, we show them all
   * in one go, by calling gtk_widget_show_all() on the window.
   * This call recursively calls gtk_widget_show() on all widgets
   * that are contained in the window, directly or indirectly.
   */
    gtk_widget_show_all(game_info->game->window);
}

void draw_wait(struct game_info *game_info) {
    struct board *board = game_info->game->board;
    GtkWidget *button;
    gtk_container_remove(GTK_CONTAINER(game_info->game->window), game_info->game->grid);
    game_info->game->grid = gtk_grid_new();
    gtk_container_add(GTK_CONTAINER(game_info->game->window), game_info->game->grid);
    char tmp[128];
    sprintf(tmp, "Now, it's player %d's turn!", board->next_player->id);
    button = gtk_button_new_with_label(tmp);
    g_signal_connect(button, "clicked", G_CALLBACK(wait_end), game_info);
    gtk_grid_attach(GTK_GRID(game_info->game->grid), button, 0, 0, 1, 1);
    gtk_widget_show_all(game_info->game->window);
}

void draw(struct player *players, struct board *board, struct game_info *game_info) {
    struct args args = game_info->game->args;
    gtk_container_remove(GTK_CONTAINER(game_info->game->window), game_info->game->grid);
    game_info->game->grid = gtk_grid_new();
    gtk_container_add(GTK_CONTAINER(game_info->game->window), game_info->game->grid);

    GtkWidget *button = gtk_button_new_with_label("Stock pile");
    gtk_widget_set_sensitive(button, FALSE);
    // g_signal_connect(button, "clicked", G_CALLBACK(play_gui_demo), game_info);
    gtk_grid_attach(GTK_GRID(game_info->game->grid), button, 0, 0, 1, 1);

    button = gtk_button_new_with_label("Discard");
    gtk_widget_set_sensitive(button, FALSE);
    gtk_grid_attach(GTK_GRID(game_info->game->grid), button, 0, 1, 1, 1);

    for (int i = 0; i < board->stock_pile_count; i++) {
        GtkWidget *button;
        if (i >= 12) {
            button = gtk_button_new_with_label("...");
            gtk_widget_set_sensitive(button, FALSE);
            gtk_grid_attach(GTK_GRID(game_info->game->grid), button, i + 1, 0, 1, 1);
            break;
        }
        char tmp[20];
        card c = board->stock_pile[board->stock_pile_count - 1 - i];
        if (args.mode == MODE_DEMO) {
            card_name(tmp, c);
            button = gtk_button_new();
            sprintf(tmp, "res/%d_s.png", c.order);
            GtkWidget *image = gtk_image_new_from_file(tmp);
            gtk_button_set_image((GtkButton *)button, image);
        } else {
            strcpy(tmp, "--");
            button = gtk_button_new_with_label(tmp);
        }
        gtk_widget_set_sensitive(button, FALSE);
        gtk_grid_attach(GTK_GRID(game_info->game->grid), button, i + 1, 0, 1, 1);
        // g_signal_connect(button, "clicked", G_CALLBACK(play_gui_demo), &(game_info->game_info[c.order]));
    }

    for (int i = 0; i < board->discard_count; i++) {
        GtkWidget *button;
        if (i >= 12) {
            button = gtk_button_new_with_label("...");
            gtk_grid_attach(GTK_GRID(game_info->game->grid), button, i + 1, 1, 1, 1);
            break;
        }
        char tmp[20];
        card c = board->discard[board->discard_count - 1 - i];
        button = gtk_button_new();
        sprintf(tmp, "res/%d_s.png", c.order);
        GtkWidget *image = gtk_image_new_from_file(tmp);
        gtk_button_set_image((GtkButton *)button, image);
        if (i > 0) gtk_widget_set_sensitive(button, FALSE);
        gtk_grid_attach(GTK_GRID(game_info->game->grid), button, i + 1, 1, 1, 1);
        // g_signal_connect(button, "clicked", G_CALLBACK(play_gui_demo), &(game_info->game_info[c.order]));
    }

    for (int i = 0; i < args.n; i++) {
        char tmp[20];
        if (i == board->next_player->id)
            sprintf(tmp, "Player %d*", i);
        else
            sprintf(tmp, "Player %d", i);
        GtkWidget *button = gtk_button_new_with_label(tmp);
        gtk_widget_set_sensitive(button, FALSE);
        gtk_grid_attach(GTK_GRID(game_info->game->grid), button, 0, i + 2, 1, 1);
        bool can_play = false;
        for (int j = 0; j < players[i].card_count; j++) {
            GtkWidget *button;
            char tmp[24];
            card c = players[i].cards[j];
            if (args.mode == MODE_DEMO || i == board->next_player->id) {
                card_name(tmp, c);
                button = gtk_button_new();
                sprintf(tmp, "res/%d_s.png", c.order);
                GtkWidget *image = gtk_image_new_from_file(tmp);
                gtk_button_set_image((GtkButton *)button, image);
            } else {
                button = gtk_button_new_with_label("--");
            }

            gtk_grid_attach(GTK_GRID(game_info->game->grid), button, j + 2, i + 2, 1, 1);
            if (i == board->next_player->id && board_can_play(board, c)) {
                g_signal_connect(button, "clicked", G_CALLBACK(play_gui_demo), &(game_info->game_info[c.order]));
                can_play = true;
            } else {
                gtk_widget_set_sensitive(button, FALSE);
            }
        }
        button = gtk_button_new_with_label("PASS");
        gtk_grid_attach(GTK_GRID(game_info->game->grid), button, 1, i + 2, 1, 1);
        if (i == board->next_player->id && !can_play) {
            g_signal_connect(button, "clicked", G_CALLBACK(play_gui_demo), &(game_info->game_info[PASS]));
        } else {
            gtk_widget_set_sensitive(button, FALSE);
        }
    }

    gtk_widget_show_all(game_info->game->window);
}

static void
activate(GtkApplication *app,
         gpointer data) {
    struct game_info *game_info = (struct game_info *)data;
    GtkWidget *window;
    GtkWidget *grid;

    /* create a new window, and set its title */
    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Window");
    gtk_window_set_default_size(GTK_WINDOW(window), 1280, 720);
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);

    /* Here we construct the container that is going pack our buttons */
    grid = gtk_grid_new();
    game_info->game->window = window;
    game_info->game->grid = grid;
    draw_welcome(game_info);
}

int gui_main(struct player *players, struct board *board, struct args args) {
    GtkApplication *app;
    int status;

    struct game game;
    game.players = players;
    game.board = board;
    game.args = args;

    struct game_info game_info[64];
    for (int i = 0; i < 54; i++) {
        game_info[i].game_info = game_info;
        game_info[i].game = &game;
        game_info[i].card_idx = i;
    }

    app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(activate), &game_info[INIT]);
    status = g_application_run(G_APPLICATION(app), 0, NULL);
    g_object_unref(app);

    return status;
}
